/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.io;

import java.io.Writer;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGParameterList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.MethodBodyList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.model.CGMethod;
import org.jfab.core.model.CGType;

/**
 * Provides a method writer.
 */
public final class CGMethodWriter implements CGWriter<CGMethod>
{
    /** Basic code generator. */
    private final BasicCodeGenerator _basicCodeGenerator;

    /** Method declaration writer. */
    private final CGMethodDeclarationWriter _methodDeclarationWriter;

    /**
     * Construct this object with the given parameters.
     * 
     * @param basicCodeGenerator Basic code generator.
     * @param methodDeclarationWriter Method declaration writer.
     */
    public CGMethodWriter(final BasicCodeGenerator basicCodeGenerator,
            final CGMethodDeclarationWriter methodDeclarationWriter)
    {
        _basicCodeGenerator = basicCodeGenerator;
        _methodDeclarationWriter = methodDeclarationWriter;
    }

    /**
     * Write a method.
     * 
     * @param writer Writer.
     * @param method Method.
     */
    @Override
    public final void write(final Writer writer, final CGMethod method)
    {
        Validate.notNull(method, "method is null");

        final CommentList commentLines = new CommentList(
                method.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                method.getAnnotations());
        final ModifierList modifiers = new ModifierList(method.getModifiers());
        final CGType returnType = method.getReturnType();
        final String methodName = method.getName();
        final CGParameterList parameters = new CGParameterList(
                method.getParameters());
        final CGTypeList throwsTypes = new CGTypeList(method.getThrowsTypes());
        final MethodBodyList methodBodyLines = new MethodBodyList(
                method.getMethodBodyLines());

        getBasicCodeGenerator().writeMultilineJavadocComment(writer,
                commentLines);

        final boolean isAbstract = modifiers.isAbstract();
        final boolean isSemicolonAdded = isAbstract;
        getMethodDeclarationWriter().writeMethodDeclaration(writer,
                annotations, modifiers, returnType, methodName, parameters,
                throwsTypes, isSemicolonAdded);

        if (!isAbstract)
        {
            writeMethodBody(writer, methodBodyLines);
        }
    }

    /**
     * @return the methodDeclarationWriter
     */
    protected CGMethodDeclarationWriter getMethodDeclarationWriter()
    {
        return _methodDeclarationWriter;
    }

    /**
     * @return the basicCodeGenerator
     */
    private BasicCodeGenerator getBasicCodeGenerator()
    {
        return _basicCodeGenerator;
    }

    /**
     * @param writer Writer.
     * @param methodBodyLines Method body lines.
     */
    private void writeMethodBody(final Writer writer,
            final MethodBodyList methodBodyLines)
    {
        getBasicCodeGenerator().writeOpenBrace(writer);

        if (CollectionUtils.isNotEmpty(methodBodyLines))
        {
            final int size = methodBodyLines.size();

            for (int i = 0; i < size; i++)
            {
                getBasicCodeGenerator().writeLine(writer,
                        methodBodyLines.get(i));
            }
        }

        getBasicCodeGenerator().writeCloseBrace(writer);
    }
}
